package com.ruoyi.system.service.impl;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.ruoyi.common.constant.WorkFlowConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.WorkFlowException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.utils.BpmnModelUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.system.domain.TemplateGroup;
import com.ruoyi.system.domain.dto.FlowEngineDTO;
import com.ruoyi.system.domain.dto.json.ChildNode;
import com.ruoyi.system.domain.dto.json.SettingsInfo;
import com.ruoyi.system.mapper.TemplateGroupMapper;
import org.apache.commons.compress.utils.Lists;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.delegate.ExecutionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.mapper.ProcessTemplatesMapper;
import com.ruoyi.system.domain.ProcessTemplates;
import com.ruoyi.system.service.IProcessTemplatesService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.flowable.engine.RepositoryService;

import javax.annotation.Resource;


/**
 * 审批模板Service业务层处理
 *
 * @author ruoyi
 * @date 2024-03-27
 */
@Service
public class ProcessTemplatesServiceImpl implements IProcessTemplatesService
{
    @Autowired
    private ProcessTemplatesMapper processTemplatesMapper;
    @Autowired
    private TemplateGroupMapper templateGroupMapper;
    @Autowired
    private RepositoryService repositoryService;


    /**
     * 查询审批模板
     *
     * @param templateId 审批模板主键
     * @return 审批模板
     */
    @Override
    public ProcessTemplates selectProcessTemplatesByTemplateId(String templateId)
    {
        return processTemplatesMapper.selectProcessTemplatesByTemplateId(templateId);
    }

    /**
     * 查询审批模板列表
     *
     * @param processTemplates 审批模板
     * @return 审批模板
     */
    @Override
    public List<ProcessTemplates> selectProcessTemplatesList(ProcessTemplates processTemplates)
    {
        List<ProcessTemplates> processTemplatesList = processTemplatesMapper.selectProcessTemplatesList(processTemplates);
        for(ProcessTemplates template : processTemplatesList){
            template.setFormName(template.getTemplateName());
        }
        return processTemplatesList;
    }

    /**
     * 新增审批模板
     *
     * @param flowEngineDTO 表单模板DTO
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult insertProcessTemplates(FlowEngineDTO flowEngineDTO)
    {
        //解析前端传参
        String processJson = flowEngineDTO.getProcess();
        ChildNode childNode = JSONObject.parseObject(processJson, new TypeReference<ChildNode>(){});
        String settingsJson = flowEngineDTO.getSettings();
        SettingsInfo settingsInfo = JSONObject.parseObject(settingsJson, new TypeReference<SettingsInfo>() {});
        String remark = flowEngineDTO.getRemark();
        String formItems = flowEngineDTO.getFormItems();
        String formName = flowEngineDTO.getFormName();
        String logo = flowEngineDTO.getLogo();
        Integer groupId = flowEngineDTO.getGroupId();
        String date = DateUtils.dateTimeNow();
        String templateId = date + IdUtils.fastSimpleEnglishUUID(5);

        //构建对象，添加表单模板
        ProcessTemplates processTemplates = new ProcessTemplates();
        processTemplates.setTemplateId(templateId);
        processTemplates.setTemplateName(formName);
        processTemplates.setGroupId(Long.valueOf(groupId));
        processTemplates.setFormItems(formItems);
        processTemplates.setProcess(processJson);
        processTemplates.setIcon(logo);
        processTemplates.setBackground(logo);
//        processTemplates.setNotify(settingsInfo.getNotify().toJSONString());
        String adminInfo = JSONObject.toJSONString(settingsInfo.getAdmin());
        processTemplates.setSettings(settingsJson);
        processTemplates.setWhoCommit(adminInfo);
        processTemplates.setWhoEdit(adminInfo);
        processTemplates.setWhoExport(adminInfo);
        processTemplates.setRemark(flowEngineDTO.getRemark());
        processTemplates.setIsStop("1");//使用
        processTemplates.setCreateTime(DateUtils.getNowDate());
        processTemplates.setMenuId(flowEngineDTO.getMenuId());
        processTemplates.setEndMsg(flowEngineDTO.getEndMsg());
        processTemplates.setRefusalMsg(flowEngineDTO.getRefusalMsg());
        processTemplates.setRepealMsg(flowEngineDTO.getRefusalMsg());
        processTemplates.setTimeOutMsg(flowEngineDTO.getTimeOutMsg());
        processTemplates.setStartMsg(flowEngineDTO.getStartMsg());
        processTemplates.setTimeLimit(flowEngineDTO.getTimeLimit());
        processTemplates.setPassRepealMsg(flowEngineDTO.getPassRepealMsg());
        processTemplatesMapper.insertProcessTemplates(processTemplates);

        //添加分组表单关系
        TemplateGroup templateGroup = new TemplateGroup();
        String processTemplateId = processTemplates.getTemplateId();
        templateGroup.setTemplateId(processTemplateId);
        Long processGroupId = processTemplates.getGroupId();
        templateGroup.setGroupId(processGroupId);
        TemplateGroup templateGroupVo = templateGroupMapper.selectTemplateGroupSortMax(templateGroup);
        if(!ObjectUtils.isEmpty(templateGroupVo)){
            String sortNum = templateGroupVo.getSortNum();
            Integer integer = Integer.valueOf(sortNum);
            int resultNum = integer + 1;
            templateGroup.setSortNum(String.valueOf(resultNum));
        }else{
            templateGroup.setSortNum("1");
        }
        templateGroupMapper.insertTemplateGroup(templateGroup);

        //配置表单json转bpmn
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("processJson", processJson);
        jsonObject.put("formJson", formItems);
        BpmnModel bpmnModel = assemBpmnModel(jsonObject, childNode, remark, formName, groupId,
                templateId);
        //部署流程
        repositoryService.createDeployment()
                //源码里规定后缀必须是.bpmn结尾或者.bpmn20.xml结尾
                //如果不是这两个结尾会造成act_re_deployment部署表里有数据，act_re_procdef流程定义表里没有数据
                .addBpmnModel(formName + ".bpmn", bpmnModel)
                .name(formName)
                .category(groupId + "")
                .deploy();
        return AjaxResult.success("添加成功");
    }

    /**
     * @param jsonObject 包含流程相关processJson 和 自动推拽表单formJson信息
     * @param childNode processJson流程相关信息
     * @param remark   表单备注
     * @param formName 表单名称
     * @param groupId  分组id
     * @param templateId  表单id
     * @return
     */
    //json转bpmn
    private BpmnModel assemBpmnModel(JSONObject jsonObject, ChildNode childNode, String remark,
                                     String formName, Integer groupId, String templateId)
    {
        //创建bpmn文件并设置头信息
        BpmnModel bpmnModel =new BpmnModel();
        //设置bpmn文件definitions标签的targetNamespace属性
        bpmnModel.setTargetNamespace(groupId+"");

        //设置Process标签
        //1、设置process标签头信息
        ExtensionAttribute extensionAttribute=new ExtensionAttribute();
        extensionAttribute.setName("DingDing");
        extensionAttribute.setNamespace("http://flowable.org/bpmn");
        extensionAttribute.setValue(jsonObject.toJSONString());
        //process标签
        Process process =new Process();
        //process标签的id属性
        process.setId(WorkFlowConstants.PROCESS_PREFIX+templateId);
        //process标签的name属性
        process.setName(formName);
        ////process标签documentation属性
        process.setDocumentation(remark);
        process.addAttribute(extensionAttribute);
        //bpmn文件中添加process标签
        bpmnModel.addProcess(process);


        //2、设置process标签中startEvent标签
        StartEvent startEvent = BpmnModelUtils.createStartEvent();
        //bpmn文件中添加startEvent标签
        process.addFlowElement(startEvent);

        //3、设置process标签中sequenceFlow标签
        List<SequenceFlow> sequenceFlows = Lists.newArrayList();
        Map<String,ChildNode> childNodeMap=new HashMap<>();
        String lastNode = null;
        try {
            lastNode = BpmnModelUtils.create(startEvent.getId(), childNode,process,bpmnModel,sequenceFlows,childNodeMap);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new WorkFlowException("操作失败");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new WorkFlowException("操作失败");
        }

        //4、设置process标签中endEvent标签
        EndEvent endEvent = BpmnModelUtils.createEndEvent();
        process.addFlowElement(endEvent);
        //
        process.addFlowElement(BpmnModelUtils.connect(lastNode, endEvent.getId(),sequenceFlows,childNodeMap,process));
        //4.2 生成结束对应的executionListener
        List<FlowableListener> executionListeners =new ArrayList<>();
        FlowableListener flowableListener=new FlowableListener();
        flowableListener.setEvent(ExecutionListener.EVENTNAME_END);
        flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        flowableListener.setImplementation("${processListener}");
        executionListeners.add(flowableListener);
        process.setExecutionListeners(executionListeners);
        new BpmnAutoLayout(bpmnModel).execute();
        return bpmnModel;
    }

    /**
     * 修改审批模板
     *
     * @param template 审批模板
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult updateProcessTemplates(ProcessTemplates template)
    {
        //构建参数
       SettingsInfo settingsInfo = JSONObject.parseObject(template.getSettings(), new TypeReference<SettingsInfo>() {});
        ProcessTemplates processTemplates = processTemplatesMapper.selectProcessTemplatesByTemplateId(template.getFormId());
        processTemplates.setTemplateName(template.getFormName());
        processTemplates.setGroupId(template.getGroupId());
        processTemplates.setFormItems(template.getFormItems());
        processTemplates.setSettings(template.getSettings());
        processTemplates.setProcess(template.getProcess());
        processTemplates.setIcon(template.getIcon());
        processTemplates.setBackground(template.getBackground());
//        processTemplates.setNotify(settingsInfo.getNotify().toJSONString());
        String adminInfo = JSONObject.toJSONString(settingsInfo.getAdmin());
        processTemplates.setWhoCommit(adminInfo);

        processTemplates.setWhoEdit(adminInfo);
        processTemplates.setWhoExport(adminInfo);
        processTemplates.setRemark(template.getRemark());
        processTemplates.setUpdateTime(DateUtils.getNowDate());
        processTemplates.setMenuId(template.getMenuId());
        processTemplates.setEndMsg(template.getEndMsg());
        processTemplates.setRefusalMsg(template.getRefusalMsg());
        processTemplates.setRepealMsg(template.getRefusalMsg());
        processTemplates.setTimeOutMsg(template.getTimeOutMsg());
        processTemplates.setStartMsg(template.getStartMsg());
        processTemplates.setTimeLimit(template.getTimeLimit());
        processTemplates.setPassRepealMsg(template.getPassRepealMsg());
        processTemplatesMapper.updateProcessTemplates(processTemplates);

        //JSON转bpmn
        ChildNode childNode = JSONObject.parseObject(template.getProcess(), new TypeReference<ChildNode>(){});
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("processJson",template.getProcess());
        jsonObject.put("formJson",template.getFormItems());
        Integer groupId = template.getGroupId().intValue();
        BpmnModel bpmnModel = assemBpmnModel(jsonObject, childNode, template.getRemark(),
                template.getFormName(), groupId, template.getFormId());

        //重新部署
        repositoryService.createDeployment()
                .addBpmnModel(template.getFormName() + ".bpmn", bpmnModel)
                .name(template.getFormName())
                .category(groupId + "")
                .deploy();
        return AjaxResult.success("修改成功");
    }

    /**
     * 修改审批模板状态（0：停用 1：启用）
     *
     * @param processTemplates 审批模板
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateProcessTemplatesStatus(ProcessTemplates processTemplates)
    {

        processTemplates.setUpdateTime(DateUtils.getNowDate());
        return processTemplatesMapper.updateProcessTemplates(processTemplates);
    }

    /**
     * 移动
     * @param processTemplates 审批模板
     * @return 结果
     */
    @Override
    public int updateProcessTemplatesGroup(ProcessTemplates processTemplates){
        TemplateGroup templateGroup = new TemplateGroup();
        templateGroup.setGroupId(processTemplates.getGroupId());
        templateGroup.setTemplateId(processTemplates.getTemplateId());
        int i = templateGroupMapper.updateTemplateGroupByTemplate(templateGroup);
        return i;
    }

    /**
     * 删除审批模板信息
     *
     * @param templateId 审批模板主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteProcessTemplatesByTemplateId(String templateId)
    {
        int i = processTemplatesMapper.deleteProcessTemplatesByTemplateId(templateId);
        //删除分组下的模型
        TemplateGroup templateGroup = new TemplateGroup();
        templateGroup.setTemplateId(templateId);
        List<TemplateGroup> templateGroups = templateGroupMapper.selectTemplateGroupList(templateGroup);
        for(TemplateGroup templateGroupVo : templateGroups){
            Long id = templateGroupVo.getId();
            templateGroupMapper.deleteTemplateGroupById(id);
        }
        return i;
    }

    /**
     * 查询菜单是否已经有关联的模板
     *
     * @param menuId 菜单id
     * @return 结果
     */
    @Override
    public AjaxResult selectProcessTemplatesByMenuId(Long menuId){
        ProcessTemplates templates = processTemplatesMapper.selectProcessTemplatesByMenuId(menuId);
        if(ObjectUtils.isEmpty(templates)){
            //菜单没有对应模板
            return AjaxResult.success("0");
        }else{
            //该菜单已有对应的流程图模板！请重新选择关联菜单
            return AjaxResult.success("1");
        }
    }
}
